package com.example.lottery.service.impl;

import com.example.lottery.common.errorcode.ServiceErrorCodeConstants;
import com.example.lottery.common.exception.ServiceException;
import com.example.lottery.common.utils.JacksonUtil;
import com.example.lottery.common.utils.RedisUtil;
import com.example.lottery.controller.param.CreateActivityParam;
import com.example.lottery.controller.param.CreatePrizeByActivityParam;
import com.example.lottery.controller.param.CreateUserByActivityParam;
import com.example.lottery.controller.param.PageParam;
import com.example.lottery.dao.dataobject.*;
import com.example.lottery.dao.mapper.*;
import com.example.lottery.service.ActivityService;
import com.example.lottery.service.dto.*;
import com.example.lottery.service.enums.ActivityPrizeStatusEnum;
import com.example.lottery.service.enums.ActivityPrizeTierEnum;
import com.example.lottery.service.enums.ActivityStatusEnum;
import com.example.lottery.service.enums.ActivityUserStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ActivityServiceImpl implements ActivityService {
    private static final String ACTIVITY_PREFIX = "ACTIVITY_";
    private static final Long ACTIVITY_TIMEOUT = 60 * 60 * 24 * 3L;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityUserMapper activityUserMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public CreateActivityDTO createActivity(CreateActivityParam param) {
        //校验活动信息是否正确
        checkActivityInfo(param);

        //保存活动信息
        ActivityDO activityDO = new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);


        //保存活动相关的奖品信息
        List<CreatePrizeByActivityParam> prizeParam = param.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOList =prizeParam.stream()
                .map(pm -> {
                    ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeAmount(pm.getPrizeAmount());
                    activityPrizeDO.setPrizeTiers(pm.getPrizeTiers());
                    activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    activityPrizeDO.setPrizeId(pm.getPrizeId());
                    return activityPrizeDO;
                }).collect(Collectors.toList());
        //批量插入
        activityPrizeMapper.batchInsert(activityPrizeDOList);


        //保存活动相关的人员信息
        List<CreateUserByActivityParam> userParams = param.getActivityUserList();
        List<ActivityUserDO> activityUserDOList = userParams.stream().map(userParam -> {
           ActivityUserDO activityUserDO = new ActivityUserDO();
           activityUserDO.setActivityId(activityDO.getId());
           activityUserDO.setUserId(userParam.getUserId());
           activityUserDO.setUserName(userParam.getUserName());
           activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
           return activityUserDO;
        }).collect(Collectors.toList());

        activityUserMapper.batchInsert(activityUserDOList);

        //整合完整活动，存放redis
        List<Long> prizeIds = param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());

        //通过prizeIds查询对应的奖品详情信息
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);

        //活动详情信息的封装
        ActivityDetailDTO activityDetailDTO = convertToActivityDetailDTO(activityDO,activityUserDOList,prizeDOList,activityPrizeDOList);

        //构造返回
        CreateActivityDTO createActivityDTO = new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDO.getId());
        return createActivityDTO;
    }


    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam param) {
        // 获取总量
        int total = activityMapper.count();

        // 获取当前页列表
        List<ActivityDO> activityDOList = activityMapper.selectActivityList(param.offset(), param.getPageSize());
        List<ActivityDTO> activityDTOList = activityDOList.stream()
                .map(activityDO -> {
                    ActivityDTO activityDTO = new ActivityDTO();
                    activityDTO.setActivityId(activityDO.getId());
                    activityDTO.setActivityName(activityDO.getActivityName());
                    activityDTO.setDescription(activityDO.getDescription());
                    activityDTO.setStatus(ActivityStatusEnum.getByName(activityDO.getStatus()));
                    return activityDTO;
                }).collect(Collectors.toList());
        return new PageListDTO<>(total, activityDTOList);
    }

    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        if (null == activityId) {
            log.error("查询活动详细信息失败,activityId:{}", activityId);
            return null;
        }

        //查询redis
        ActivityDetailDTO detailDTO = getActivityFromCache(activityId);
        if (null != detailDTO) {
            log.info("查询活动详细信息成功,detailDTO:{}", JacksonUtil.writeValueAsString(detailDTO));
            return detailDTO;
        }

        //如果redis不存在，查表MySQL
        //活动表
        ActivityDO activityDO = activityMapper.selectById(activityId);


        //活动奖品表(只存放了奖品有哪些，并无奖品的详情信息)
        List<ActivityPrizeDO> prizeDO = activityPrizeMapper.selectByActivityId(activityId);

        //活动人员表
        List<ActivityUserDO> userDo = activityUserMapper.selectByActivityId(activityId);

        //奖品表(奖品的详情信息获取)
        List<Long> prizeIds = prizeDO.stream().map(ActivityPrizeDO::getPrizeId).collect(Collectors.toList());
        List<PrizeDO> pDOList = prizeMapper.batchSelectByIds(prizeIds);

        //整合活动详细信息，存放redis
        detailDTO = convertToActivityDetailDTO(activityDO,userDo,pDOList,prizeDO);
        cacheActivity(detailDTO);

        //返回
        return detailDTO;
    }


    @Override
    public void cacheActivity(Long activityId) {
        if (activityId == null) {
            log.warn("缓存的的活动id为空");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }

        //查询表数据
        ActivityDO activityDO = activityMapper.selectById(activityId);
        if (activityDO == null) {
            log.warn("缓存的活动id有误");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_ERROR);
        }

        //活动奖品表
        List<ActivityPrizeDO> prizeDO = activityPrizeMapper.selectByActivityId(activityId);
        //活动人员表
        List<ActivityUserDO> userDo = activityUserMapper.selectByActivityId(activityId);
        //奖品表
        List<Long> prizeIds = prizeDO.stream().map(ActivityPrizeDO::getPrizeId).collect(Collectors.toList());
        List<PrizeDO> pDOList = prizeMapper.batchSelectByIds(prizeIds);

        //整合活动详细信息，存放redis
        cacheActivity(convertToActivityDetailDTO(activityDO, userDo, pDOList, prizeDO));
    }


    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO,
                                                         List<ActivityUserDO> activityUserDOList,
                                                         List<PrizeDO> prizeDOList,
                                                         List<ActivityPrizeDO> activityPrizeDOList) {
        //活动自身信息的转换
        ActivityDetailDTO activityDetailDTO = new ActivityDetailDTO();
        activityDetailDTO.setActivityId(activityDO.getId());
        activityDetailDTO.setActivityName(activityDO.getActivityName());
        activityDetailDTO.setDesc(activityDO.getDescription());
        activityDetailDTO.setStatus(ActivityStatusEnum.getByName(activityDO.getStatus()));

        //活动详情中的静态奖品类的详情信息列表
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList1 = activityPrizeDOList.stream()
                .map(apDO -> {
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    //先获取活动中奖品的id
                    prizeDTO.setPrizeId(apDO.getPrizeId());

                    //奖品的详情信息是在prize表中，所以此处获取的只是活动相关联的奖品信息，并没有奖品的详情信息，所以还需要通过prizeDOList来获取
                    //此处通过一个容器，来获取对应id的奖品详情信息，因为ids是列表，所以获取的奖品信息也是列表，此处使用一个容器，可以防止获取的详情信息为空的情况
                    Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream().filter(
                            prizeDO -> prizeDO.getId().equals(apDO.getPrizeId()))
                            .findFirst();

                    //如果存在，则将奖品的详细信息进行存入prizeDTO中
                    optionalPrizeDO.ifPresent(prizeDO -> {
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
                        prizeDTO.setPrice(prizeDO.getPrice());
                        prizeDTO.setDescription(prizeDO.getDescription());
                    });

                    prizeDTO.setTiers(ActivityPrizeTierEnum.getByName(apDO.getPrizeTiers()));
                    prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.getByName(apDO.getStatus()));
                    return prizeDTO;
                }).collect(Collectors.toList());

        activityDetailDTO.setPrizeListDTO(prizeDTOList1);

        List<ActivityDetailDTO.UserDTO> userDTOList = activityUserDOList.stream().map(audo->{
                    ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                    userDTO.setUserId(audo.getUserId());
                    userDTO.setUserName(audo.getUserName());
                    userDTO.setStatus(ActivityUserStatusEnum.getByName(audo.getStatus()) );
                    return userDTO;
                }).collect(Collectors.toList());

        activityDetailDTO.setUserListDTO(userDTOList);

        //使用redis缓存活动详情信息
        cacheActivity(activityDetailDTO);

        return activityDetailDTO;
    }

    private void cacheActivity(ActivityDetailDTO activityDetailDTO) {
        //缓存完整的活动信息
        if (activityDetailDTO == null || activityDetailDTO.getActivityId() == null) {
            log.warn("要缓存的活动信息不存在");
        }
        try {
            redisUtil.set(ACTIVITY_PREFIX+activityDetailDTO.getActivityId(), JacksonUtil.writeValueAsString(activityDetailDTO),ACTIVITY_TIMEOUT);
        }catch (Exception e){
            log.error("缓存活动异常，ActivityDetailDTO={}",JacksonUtil.writeValueAsString(activityDetailDTO),e);
        }

    }

    /**
     * 从redis缓存中获取活动详情
     * @param activityId
     * @return
     */
    private ActivityDetailDTO getActivityFromCache(Long activityId) {
        if (null == activityId) {
            log.warn("获取缓存活动数据的activityId为空！");
            return null;
        }

        try {
            String str = redisUtil.get(ACTIVITY_PREFIX+activityId);
            if (!StringUtils.hasText(str)) {
                log.warn("获取缓存活动中的数据的ActivityId为空！");
                return null;
            }
            ActivityDetailDTO detailDTO = JacksonUtil.readValue(str, ActivityDetailDTO.class);
            return detailDTO;
        }catch (Exception e){
            log.error("从缓存中获取异常失败！");
            return null;
        }
    }

    private void checkActivityInfo(CreateActivityParam param) {
        if (param == null) {
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }

        //校验人员Id是否存在
        List<Long> userIds = param.getActivityUserList()
                .stream()
                .map(CreateUserByActivityParam::getUserId)
                .distinct()
                .collect(Collectors.toList());

        //通过userIds从数据库中查询用户Id列表
        List<Long> existUserIds = userMapper.selectExistByIds(userIds);
        if (CollectionUtils.isEmpty(existUserIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }

        //再次判断前端传递的用户是否真的存在与数据库
        userIds.forEach(id -> {
            if (!existUserIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });


        //校验奖品Id是否存在
        List<Long> prizeIds = param.getActivityPrizeList()
                .stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()//去重
                .collect(Collectors.toList());

        List<Long> existPrizeIds = prizeMapper.selectExistByIds(prizeIds);
        if (CollectionUtils.isEmpty(existPrizeIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }

        prizeIds.forEach(id -> {
            if (!existPrizeIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });


        //校验人员数量大于等于奖品数量
        int userAmount = param.getActivityUserList().size();
        long prizeAmount = param.getActivityPrizeList()
                .stream()
                .mapToLong(CreatePrizeByActivityParam::getPrizeAmount)
                .sum();
        if (userAmount < prizeAmount) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_PRIZE_AMOUNT_ERROR);
        }

        //校验活动奖品等有效性
        param.getActivityPrizeList().forEach(prize -> {
           if (null == ActivityPrizeTierEnum.getByName(prize.getPrizeTiers())) {
               throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
           }
        });

    }


}
